# push/adapters/unipush.py
import requests
import json
import time
import uuid
import hashlib
import os
from datetime import date
from typing import Dict
from config.settings import settings
from core.exceptions import PushServiceError

class UniPushAdapter:

    # ... existing code ...
    def push_by_template(self, cid: str, template_data: dict, url: str = "") -> dict:
        """使用模板数据向单个设备推送消息"""
        title = template_data.get('first', '')
        content = f"{template_data.get('keyword1', '')} {template_data.get('keyword2', '')} {template_data.get('remark', '')}"
        
        return self.push_to_single_device(cid, title, content, url)
    
    def __init__(self):
        self.app_id = settings.UNIPUSH_APP_ID
        self.app_key = settings.UNIPUSH_APP_KEY
        self.app_secret = settings.UNIPUSH_APP_SECRET
        
        # 替换URL中的占位符
        self.auth_base_url = settings.PUSH_AUTH_BASE_URL.replace('$appId', self.app_id)
        self.api_base = settings.PUSH_API_BASE_URL.replace('$appId', self.app_id)
        
        print(f"初始化适配器 - AppID: {self.app_id}")
        print(f"鉴权URL: {self.auth_base_url}")
        print(f"推送API URL: {self.api_base}")
        
        self.token = ""
        self.token_expire = 0
        self.token_request_count = self._load_token_request_count()
        self.failed_401_count = 0  # 记录401错误次数

    def _load_token_request_count(self):
        count_file = 'token_request_count.json'
        today = str(date.today())
        if os.path.exists(count_file):
            with open(count_file, 'r') as f:
                try:
                    data = json.load(f)
                    if data.get('date') == today:
                        return data.get('count', 0)
                except json.JSONDecodeError:
                    pass
        return 0

    def _save_token_request_count(self):
        count_file = 'token_request_count.json'
        today = str(date.today())
        with open(count_file, 'w') as f:
            json.dump({'date': today, 'count': self.token_request_count}, f)

    def get_access_token(self) -> str:
        """获取API访问令牌（优化Token管理）"""
        # 检查Token有效性（提前60秒刷新）
        is_token_valid = (
            self.token 
            and time.time() < self.token_expire - 60
        )
        
        if is_token_valid:
            print(f"✅ 使用内存Token（剩余有效期: {int(self.token_expire - time.time())}秒）")
            return self.token
        
        # 检查每分钟调用次数
        if self.token_request_count >= 100:
            print("🚫 鉴权接口每分钟调用次数已达上限，暂时无法请求新Token")
            raise PushServiceError("鉴权接口每分钟调用次数已达上限")
        
        # 检查每天调用次数（持久化记录）
        if self.token_request_count >= 100000:
            print("🚫 鉴权接口每天调用次数已达上限，暂时无法请求新Token")
            raise PushServiceError("鉴权接口每天调用次数已达上限")
        
        self.token_request_count += 1
        self._save_token_request_count()
        print(f"🔄 第{self.token_request_count}次请求Token...")
        
        # 发送鉴权请求
        url = f"{self.auth_base_url}/auth"
        timestamp = str(int(time.time() * 1000))
        
        # 计算签名
        sign_text = f"{self.app_key}{timestamp}{self.app_secret}"
        sign = hashlib.sha256(sign_text.encode('utf-8')).hexdigest()
        
        payload = {
            "appkey": self.app_key,
            "timestamp": timestamp,
            "sign": sign
        }
        
        print(f"签名原始字符串: {sign_text}")
        print(f"SHA256签名结果: {sign}")
        print(f"鉴权请求参数: {payload}")

        # 验证时间戳
        current_time = time.time()
        timestamp_sec = int(timestamp) / 1000
        time_diff = abs(current_time - timestamp_sec)
        if time_diff > 300:
            print(f"⚠️ 时间戳与本地时间差异过大 ({time_diff}秒)")
        
        try:
            response = requests.post(url, json=payload, headers={"Content-Type": "application/json"})
            response.raise_for_status()
            data = response.json()
            print(f"鉴权响应: {data}")
            
            if data.get("code") == 0:
                token = data.get("data", {}).get("token")
                if not token:
                    raise ValueError("响应中缺少token字段")
                
                expire_time = data.get("data", {}).get("expire_time")
                self.token = token
                self.token_expire = int(expire_time) / 1000 if expire_time else time.time() + 7200
                
                print(f"🎉 成功获取Token: 有效期至 {time.ctime(self.token_expire)}")
                return token
            else:
                raise PushServiceError(f"鉴权失败: {data.get('msg', '未知错误')}")
                
        except (requests.RequestException, KeyError, ValueError) as e:
            print(f"🚫 获取Token失败: {str(e)}")
            if 'response' in locals():
                print(f"响应内容: {response.text}")
            raise

    def push_to_single_device(self, cid: str, title: str, content: str, url: str = "") -> Dict:
        """向单个设备推送消息（优化401错误处理）"""
        token = self.get_access_token()
        token_remaining = max(0, int(self.token_expire - time.time()))
        print(f"💾 当前Token状态: 剩余有效期 {token_remaining} 秒")
        
        push_url = f"{self.api_base}/push/single/cid"
        request_id = str(uuid.uuid4())
        # 构建 Android 厂商通道参数
        android_ups = {
            "notification": {
                "title": title,
                "body": content,
                "click_type": "startapp", # 点击行为 url   
                "url": url or "https://example.com"
            },
            "options": {
                "HW": {
                    "/message/android/target_user_type": 1
                }
            }
        }
        payload = {
            "request_id": request_id,
            "settings": {"ttl": 7200000},  # 消息保留2小时
            "audience": {"cid": [cid]},
            ## 系统个推通道推送参数
            "push_message": {
                "notification": {
                    "title": title,
                    "body": content,
                    "click_type": "startapp",
                    "url": url or "https://example.com"
                },
                "android": {
                    "ups": android_ups
                }
            },
            ## 厂商通道推送参数
            "push_channel": {
                "notification": {
                    "title": title,
                    "body": content,
                    "click_type": "startapp",
                    "url": url or "https://example.com"
                },
                "android": {
                    "ups": android_ups
                }
            }
        }
        
        # 请求头设置（使用token字段）
        headers = {
            "token": token,
            "Content-Type": "application/json;charset=utf-8",
            "X-Request-ID": request_id,
            "User-Agent": "UniPushAdapter/1.0",
            "Accept": "application/json"
        }
        
        print(f"🔗 推送URL: {push_url}")
        print(f"🔑 Token前10位: {token[:10]}...")
        print(f"📡 请求头: {headers}")
        print(f"📦 推送参数: {payload}")
        
        try:
            response = requests.post(push_url, headers=headers, json=payload)
            response.raise_for_status()
            data = response.json()
            print(f"📩 推送响应: {data}")
            self.failed_401_count = 0
            return data
            
        except requests.exceptions.HTTPError as e:
            if response.status_code == 401:
                self.failed_401_count += 1
                print(f"🔑 第{self.failed_401_count}次401错误: Token无效")
                
                # 强制刷新Token
                print("⏰ 被动刷新Token...")
                self.token = ""
                new_token = self.get_access_token()
                headers["token"] = new_token
                
                # 仅重试1次
                if self.failed_401_count <= 1:
                    print("🔄 重试推送...")
                    response = requests.post(push_url, headers=headers, json=payload)
                    data = response.json()
                    print(f"📩 重试响应: {data}")
                    return data
                else:
                    print("🚫 多次401错误，放弃重试")
            # 其他错误处理...
            print(f"🌐 HTTP错误: {str(e)}, 状态码: {response.status_code}")
            raise PushServiceError(f"推送异常: {str(e)}")
        except Exception as e:
            print(f"❌ 未知异常: {str(e)}")
            raise PushServiceError(f"推送失败: 未知异常 - {str(e)}")